home *** CD-ROM | disk | FTP | other *** search
/ Large Pack of OldSkool DOS MOD Trackers / package_16-january-2001.zip / Effects / cheapo dc.cpp < prev    next >
C/C++ Source or Header  |  2000-09-01  |  15KB  |  648 lines

  1. // Copyright (C) Mikko Apo (apo@iki.fi)
  2. // The following code may be used to write free software
  3. // if credit is given to the original author.
  4. // Using it for anything else is not allowed without permission
  5. // from the author.
  6.  
  7. /*
  8. Revision history:
  9.   2.01    Mono signal analysis now actually shows the real dc offset.
  10.   2.0    First release of stereo/mono-in version.
  11.   1.0    First release. Mono in only.
  12.   */
  13.  
  14.  
  15. #include <stdlib.h>
  16. #include <time.h>
  17. #include <math.h>
  18. #include <string.h>
  19. #include "../mdk.h"
  20.  
  21. #define miCOMMAND_STRING "Show analysis...\nReset analysis\nAbout..."
  22. #define miMACHINE_NAME "cheapo dc"
  23. #define miSHORT_NAME "ch.dc"
  24. #define miMACHINE_AUTHOR "Mikko Apo (apo@iki.fi)"
  25. #define miMAX_TRACKS        0
  26. #define miMIN_TRACKS        0
  27. #define miNUMGLOBALPARAMETERS 5
  28. #define miNUMTRACKPARAMETERS 0
  29. #define miNUMATTRIBUTES 0
  30. #define miVERSION "2.01"
  31.  
  32. //    Parameters
  33.  
  34. CMachineParameter const paraMode = 
  35. { pt_byte, "Mode","Mode: 0 Statistics & Separate controlling, 1 Stats & Left master right slave, 2 Separate, 3 Right slave",0,3,0xff,MPF_STATE,0 };
  36.  
  37. CMachineParameter const paraLDC = 
  38. { pt_word, "Left Offset","Left Offset",0,0xfffe,0xffff,MPF_STATE,0xfffe/2 };
  39.  
  40. CMachineParameter const paraRDC = 
  41. { pt_word, "Right Offset","Right Offset",0,0xfffe,0xffff,MPF_STATE,0xfffe/2 };
  42.  
  43. CMachineParameter const paraInertia = 
  44. { pt_word, "Inertia","Inertia length",0,0xfffe,0xffff,MPF_STATE,1 };
  45.  
  46. CMachineParameter const paraInertiaUnit = 
  47. { pt_byte, "Inertia Unit","Inertia Unit: 0=tick (default), 1 ticks/256, 2 samples, 3=ms, 4=seconds",0,4,0xff,MPF_STATE,0 };
  48.  
  49. // List of all parameters, track parameters last
  50.  
  51. CMachineParameter const *pParameters[] = 
  52. { ¶Mode,¶LDC,¶RDC,¶Inertia,¶InertiaUnit };
  53.  
  54. #pragma pack(1)
  55.  
  56. class gvals
  57. {
  58. public:
  59.     byte mode;
  60.     word ldc;
  61.     word rdc;
  62.     word inertia;
  63.     byte inertiaunit;
  64. };
  65.  
  66. #pragma pack()
  67.  
  68. // Machine's info
  69.  
  70. CMachineInfo const MacInfo = 
  71. {
  72.     MT_EFFECT,MI_VERSION,MIF_DOES_INPUT_MIXING,miMIN_TRACKS,miMAX_TRACKS,
  73.     miNUMGLOBALPARAMETERS,miNUMTRACKPARAMETERS,pParameters,miNUMATTRIBUTES,NULL,
  74. #ifdef _DEBUG
  75.     miMACHINE_NAME" [DEBUG]"
  76. #else
  77.     miMACHINE_NAME
  78. #endif
  79.     ,miSHORT_NAME,miMACHINE_AUTHOR,miCOMMAND_STRING
  80. };
  81.  
  82.  
  83. class miex : public CMDKMachineInterfaceEx
  84. {
  85.  
  86. };
  87.  
  88. class mi : public CMDKMachineInterface
  89. {
  90. public:
  91.     mi();
  92.  
  93.     virtual void Command(int const i);
  94.     virtual void Tick();
  95.     virtual char const *DescribeValue(int const param, int const value);
  96.  
  97.     virtual void MDKInit(CMachineDataInput * const pi);
  98.     virtual bool MDKWork(float *psamples, int numsamples, int const mode);
  99.     virtual bool MDKWorkStereo(float *psamples, int numsamples, int const mode);
  100.     virtual void MDKSave(CMachineDataOutput * const po) { }
  101.  
  102.     public:
  103.     virtual CMDKMachineInterfaceEx *GetEx() { return &ex; }
  104.     virtual void OutputModeChanged(bool stereo);
  105.  
  106.     public:
  107.     miex ex;
  108.     gvals gval;
  109.  
  110. private:
  111.  
  112.     void print_amp(char *txt, float num);
  113.     void print_dc(char *txt, double dcsum,unsigned int dc_num);
  114.     void print_suggest(char *txt, float max,float min);
  115.     void print_time(char *txt,unsigned long samples);
  116.     void print_channel(char *txt,float maxlevelfound, float minlevelfound, double dcsum);
  117.  
  118.  
  119.     unsigned long calculate_length(byte type, word len);
  120.  
  121.     float l_dc,r_dc;
  122.     double left_dcsum_in,left_dcsum_out,right_dcsum_in,right_dcsum_out;
  123.     int valInertia,valInertiaUnit;
  124.     bool l_inertia,r_inertia,first;    // inertia
  125.     float l_dc_inc,l_dc_target; //
  126.     float r_dc_inc,r_dc_target;  //
  127.     unsigned int l_counter,r_counter; //
  128.     unsigned long dc_counter;
  129.     int valMode;
  130.     bool stereo_mode;
  131.     float left_maxlevelfound,left_minlevelfound;
  132.     float right_maxlevelfound,right_minlevelfound;
  133. };
  134.  
  135.  
  136. DLL_EXPORTS
  137.  
  138. mi::mi()
  139. {
  140.     GlobalVals = &gval;
  141. }
  142.  
  143. // Produces output for analysis
  144.  
  145. void mi::print_amp(char *txt, float num)
  146. {
  147.     sprintf(txt,"%ssample:%+.1f (",txt,num);
  148.     if(num)
  149.     {
  150.       sprintf(txt,"%s%.1fdB; %+.2f%%)",txt,(float)(20.0*log10(fabs(num)/( (num>0)?32767.0:32768.0) )),(float)((100*num)/( (num>0)?32767.0:32768.0)));
  151.     } else
  152.     {
  153.       sprintf(txt,"%s-inf dB; 0.00%%)",txt);
  154.     }
  155.  
  156.     if(num>32767.0||num<-32768.0)
  157.     {
  158.         sprintf(txt,"%s *** Possible clipping ***",txt);
  159.     }
  160.     sprintf(txt,"%s\n",txt);
  161. }
  162.  
  163. void mi::print_suggest(char *txt, float max,float min)
  164. {
  165.     if(max>32767.0||min<-32768.0)
  166.     {
  167.         max=(float)fabs(max);
  168.         min=(float)fabs(min);
  169.         sprintf(txt,"%sThe analysed signal exceeds the normal range between -32768.0 and 32767.0\nIt might clip in the following effect and it will clip if connected to master output.\n",txt);
  170.         sprintf(txt,"%sThe input volume slider of the next machine should be set to under %d.\n",txt,(int)(16384*( (max > min) ? (32767.0/max) : (32768.0/min) ) ) );
  171.         sprintf(txt,"%sThe master output volume slider should have a value over ",txt);
  172.         sprintf(txt,"%s%d to prevent clipping.\n",txt,(int)(log10((max>min)?(32767.0/max):(32768.0/min))*((20.0*16384.0)/(-80.0))));
  173.     } else
  174.     {
  175.         sprintf(txt,"%sThe analysed signal was in the normal range between -32768.0 and 32767.0. \nThere is no need to adjust the volume sliders.\n",txt);
  176.     }
  177. }
  178.  
  179. void mi::print_time(char *txt,unsigned long samples)
  180. {
  181.   unsigned long secs;
  182.   unsigned long samplespersec=pMasterInfo->SamplesPerSec;
  183.   unsigned long samplespertick=pMasterInfo->SamplesPerTick;
  184.   secs=(unsigned int)(samples/samplespersec);
  185.   sprintf(txt,"%s %ld ticks [time: %d:%02d:%02d.%03d (%ld samples)]\n",txt,samples/samplespertick,secs/3600,(secs%3600)/60,secs%60,(1000*(samples%(samplespersec)))/samplespersec,samples);
  186. }
  187.  
  188. void mi::print_dc(char *txt, double dcsum,unsigned int dc_num)
  189. {
  190.     sprintf(txt,"%sDC Offset (average): %.1f (",txt,dcsum/dc_num);
  191.     if(dcsum/dc_num)
  192.     {
  193.       sprintf(txt,"%s%.1fdB; %+.2f%%)\n",txt,(float)(20.0*log10(fabs(dcsum/(dc_num*32768.0)))),(float)((100*(dcsum/dc_num))/32768.0));
  194.     } else
  195.     {
  196.       sprintf(txt,"%s-inf dB; 0.00%%)\n",txt);
  197.     }
  198. }
  199.  
  200.  
  201. void mi::print_channel(char *txt,float maxlevelfound, float minlevelfound, double dcsum)
  202. {
  203.     float dc,offset;
  204.     bool suggest=false;
  205.     sprintf(txt,"%sMax ",txt);
  206.     print_amp(txt,maxlevelfound);
  207.  
  208.     sprintf(txt,"%sMin ",txt);
  209.     print_amp(txt,minlevelfound);
  210.  
  211.     print_dc(txt,dcsum,dc_counter);
  212.  
  213.     // calculate the correct suggestion
  214.     dc=-(float) (dcsum/dc_counter);
  215.     offset=0;
  216.     sprintf(txt,"%sDC Offset Suggestion: ",txt);
  217.     if(dc)
  218.     {
  219.     if(dc>0)
  220.     {
  221.         // check if wave fits in the graph with full dc
  222.         if(maxlevelfound+dc<=32767)
  223.         {
  224.             offset=dc;
  225.             suggest=true;
  226.         } else
  227.         {
  228.             // we can't correct with full dc, so find the max correction
  229.             if(32767>maxlevelfound)
  230.             {
  231.                 offset=32767-maxlevelfound;
  232.                 suggest=true;
  233.             }
  234.         }
  235.     }
  236.     if(dc<0)
  237.     {
  238.         if(minlevelfound+dc>=-32768)
  239.         {
  240.             offset=dc;
  241.             suggest=true;
  242.         } else
  243.         {
  244.             if(-32768<minlevelfound)
  245.             {
  246.                 offset=-32768-minlevelfound;
  247.                 suggest=true;
  248.             }
  249.         }
  250.     }
  251.     if(suggest)
  252.     {
  253.       if(offset==dc)
  254.       {
  255.         sprintf(txt,"%sset increment to %+d [%d]\n",txt,(int)offset,(int)offset+32767);
  256.       } else
  257.       {
  258.         sprintf(txt,"%swithout clipping the signal, you can set the dc offset to %+d [%d]\n",txt,(int)offset,(int)offset+32767);
  259.       }
  260.     } else
  261.     {
  262.         sprintf(txt,"%scannot fix dc offset because the signal is already clipping.\n",txt);
  263.     }
  264.     } else
  265.     {
  266.         sprintf(txt,"%sno suggestion.\n",txt);
  267.     }
  268. }
  269.  
  270. void mi::Command(int const i)
  271. {
  272.     static char txt[2000];
  273.     switch(i)
  274.     {
  275.     case 0:
  276.         if(dc_counter&&valMode<=1)
  277.         {
  278.           sprintf(txt,"Sound analysis:\n\nStatistical data collected:");
  279.           print_time(txt,dc_counter);
  280.           if(stereo_mode)
  281.           {
  282.               sprintf(txt,"%s\nLeft channel:\n\n",txt);
  283.               print_channel(txt,left_maxlevelfound,left_minlevelfound, left_dcsum_in);
  284.               sprintf(txt,"%s\nRight channel:\n\n",txt);
  285.               print_channel(txt,right_maxlevelfound,right_minlevelfound, right_dcsum_in);
  286.               sprintf(txt,"%s\nDC offsets of the signals after this machine:\n\nLeft ",txt);
  287.               print_dc(txt,left_dcsum_out,dc_counter);
  288.               sprintf(txt,"%sRight ",txt);
  289.               print_dc(txt,right_dcsum_out,dc_counter);
  290.               sprintf(txt,"%s\nVolume slider suggestion based on max and min levels of both channels:\n\n",txt);
  291.               print_suggest(txt,(left_maxlevelfound>right_maxlevelfound)?left_maxlevelfound:right_maxlevelfound,(left_minlevelfound<right_minlevelfound)?left_minlevelfound:right_minlevelfound);
  292.           } else
  293.           {
  294.               sprintf(txt,"%s\nMono signal:\n\n",txt);
  295.               print_channel(txt,left_maxlevelfound,left_minlevelfound, left_dcsum_in);
  296.               sprintf(txt,"%s\nDC offset of the signal after this machine:\n\nSignal ",txt);
  297.               print_dc(txt,left_dcsum_out,dc_counter);
  298.               sprintf(txt,"%s\nVolume slider suggestion based on max and min levels:\n\n",txt);
  299.               print_suggest(txt,left_maxlevelfound,left_minlevelfound);
  300.           }
  301.         } else
  302.         {
  303.             sprintf(txt,"*** No sound analysed ***\n");
  304.         }
  305.         pCB->MessageBox(txt);
  306.         break;
  307.     case 1:
  308.         left_maxlevelfound=right_maxlevelfound=-9999999.0;
  309.         left_minlevelfound=right_minlevelfound=9999999.0;
  310.         left_dcsum_in=left_dcsum_out=right_dcsum_in=right_dcsum_out=0.0;
  311.         dc_counter=0;
  312.         break;
  313.     case 2:
  314.         pCB->MessageBox(miMACHINE_NAME"\n\nBuild date: "__DATE__"\nVersion: "miVERSION"\nCoded by: "miMACHINE_AUTHOR"\nThanks to #buzzdev for support.\n\nCheck out http://www.iki.fi/apo/buzz/\nfor more buzz stuff.\n\nExcellent skin made by Hymax.");
  315.         break;
  316.     }
  317. }
  318.  
  319. char const *mi::DescribeValue(int const param, int const value)
  320. {
  321.     static char txt[100];
  322.  
  323.     switch(param)
  324.     {
  325.     case 0:
  326.         switch(value)
  327.         {
  328.         case 0: return("Stat,Separate");
  329.         case 1: return("Stat,Right Sl");
  330.         case 2:    return("Separate");
  331.         case 3:    return("Right Slave");
  332.         }
  333.         break;
  334.     case 1:
  335.     case 2:
  336.         sprintf(txt,"%.0f",(float)(value-0xfffe/2));
  337.         break;
  338.     case 3:
  339.         sprintf(txt,"%d %s",value,DescribeValue(4,valInertiaUnit));
  340.         break;
  341.     case 4:
  342.         switch(value)
  343.         {
  344.         case 0: return("ticks");
  345.         case 1: return("ticks/256");
  346.         case 2:    return("samples");
  347.         case 3:    return("ms");
  348.         case 4: return("secs");
  349.         }
  350.         break;
  351.     }
  352.  
  353.     return txt;
  354. }
  355.  
  356. void mi::MDKInit(CMachineDataInput * const pi)
  357. {
  358.     valMode=paraMode.DefValue;
  359.     l_dc=(float)(paraLDC.DefValue-0xfffe/2);
  360.     r_dc=(float)(paraRDC.DefValue-0xfffe/2);
  361.     l_inertia=r_inertia=false;
  362.     valInertia=paraInertia.DefValue;
  363.     valInertiaUnit=paraInertiaUnit.DefValue;
  364.     first=true;
  365.     stereo_mode=false;
  366. }
  367.  
  368. void mi::OutputModeChanged(bool stereo)
  369. {
  370.     stereo_mode=stereo;
  371.     Command(1);
  372.     if(r_inertia)
  373.     {
  374.         r_dc=r_dc_target;
  375.         r_inertia=false;
  376.     }
  377. }
  378.  
  379. unsigned long mi::calculate_length(byte type, word len)
  380. {
  381.     unsigned long length;
  382.     switch(type)
  383.     {
  384.     case 0:
  385.         length=len*pMasterInfo->SamplesPerTick;
  386.         break;
  387.     case 1:
  388.         length=(len*pMasterInfo->SamplesPerTick)/256;
  389.         break;
  390.     case 2:
  391.         length=len;
  392.         break;
  393.     case 3:
  394.         length=(len*pMasterInfo->SamplesPerSec)/1000;
  395.         break;
  396.     case 4:
  397.         length=len*pMasterInfo->SamplesPerSec;
  398.         break;
  399.     }
  400.     return length;
  401. }
  402.  
  403.  
  404. void mi::Tick()
  405. {
  406.   if (gval.mode != paraMode.NoValue)
  407.   {
  408.     if(valMode!=gval.mode)
  409.     {
  410.         if(r_inertia)
  411.         {
  412.             r_dc=r_dc_target;
  413.             r_inertia=false;
  414.         }
  415.     }
  416.     valMode=gval.mode;
  417.   }
  418.   if (gval.inertia != paraInertia.NoValue)
  419.   {
  420.     valInertia=gval.inertia;
  421.   }
  422.   if (gval.inertiaunit != paraInertiaUnit.NoValue)
  423.   {
  424.     valInertiaUnit=gval.inertiaunit;
  425.   }
  426.  
  427.   if (gval.ldc != paraLDC.NoValue)
  428.   {
  429.     l_inertia=true;
  430.     l_counter=calculate_length(valInertiaUnit,valInertia);
  431.     l_dc_target=(float)(gval.ldc-0xfffe/2);
  432.     l_dc_inc=(l_dc_target-l_dc)/l_counter;
  433.   }
  434.   if (gval.rdc != paraRDC.NoValue)
  435.   {
  436.     r_inertia=true;
  437.     r_counter=calculate_length(valInertiaUnit,valInertia);
  438.     r_dc_target=(float)(gval.rdc-0xfffe/2);
  439.     r_dc_inc=(r_dc_target - r_dc)/r_counter;
  440.   }
  441.  
  442.   // bypasses inertia on the first tick() to prevent the machine warmup effect
  443.   // sets inertia to 0 so that the amp values are effective immediatly
  444.   if(first)
  445.   {
  446.     r_inertia=true;
  447.     r_counter=0;
  448.     l_inertia=true;
  449.     l_counter=0;
  450.     first=false;
  451.   }
  452.  
  453.  
  454.  
  455. bool mi::MDKWorkStereo(float *psamples, int numsamples, int const mode)
  456. {
  457.     if ((mode==WM_WRITE)||(mode==WM_NOIO))
  458.     {
  459.         return false;
  460.     }
  461.  
  462.     if (mode == WM_READ)        // <thru>
  463.         return true;
  464.  
  465.     if(valMode>=2) // no stats
  466.     {
  467.     if(valMode==3) // linked
  468.     {
  469.     do 
  470.     {
  471.         if(l_inertia)
  472.         {
  473.             l_dc+=l_dc_inc;
  474.             if(!(l_counter--))
  475.             {
  476.                 l_inertia=false;
  477.                 l_dc=l_dc_target;
  478.             }
  479.         }
  480.         psamples[0]+=l_dc;
  481.         psamples[1]+=l_dc;
  482.         psamples+=2;
  483.     } while(--numsamples);
  484.     } else    // separate
  485.     {
  486.  
  487.     do 
  488.     {
  489.         if(l_inertia)
  490.         {
  491.             l_dc+=l_dc_inc;
  492.             if(!(l_counter--))
  493.             {
  494.                 l_inertia=false;
  495.                 l_dc=l_dc_target;
  496.             }
  497.         }
  498.         psamples[0]+=l_dc;
  499.         if(r_inertia)
  500.         {
  501.             r_dc+=r_dc_inc;
  502.             if(!(r_counter--))
  503.             {
  504.                 r_inertia=false;
  505.                 r_dc=r_dc_target;
  506.             }
  507.         }
  508.         psamples[1]+=r_dc;
  509.         psamples+=2;
  510.     } while(--numsamples);
  511.     }
  512.     } else // stats
  513.     {
  514.     if(valMode==1) // linked
  515.     {
  516.     do 
  517.     {
  518.         if(l_inertia)
  519.         {
  520.             l_dc+=l_dc_inc;
  521.             if(!(l_counter--))
  522.             {
  523.                 l_inertia=false;
  524.                 l_dc=l_dc_target;
  525.             }
  526.         }
  527.         left_dcsum_in+=psamples[0];
  528.         psamples[0]+=l_dc;
  529.         left_dcsum_out+=psamples[0];
  530.         left_dcsum_in+=psamples[1];
  531.         psamples[1]+=l_dc;
  532.         left_dcsum_out+=psamples[1];
  533.         dc_counter+=2;
  534.         psamples+=2;
  535.     } while(--numsamples);
  536.     } else // separate
  537.     {
  538.  
  539.     do 
  540.     {
  541.         if(l_inertia)
  542.         {
  543.             l_dc+=l_dc_inc;
  544.             if(!(l_counter--))
  545.             {
  546.                 l_inertia=false;
  547.                 l_dc=l_dc_target;
  548.             }
  549.         }
  550.         left_dcsum_in+=psamples[0];
  551.         if(psamples[0]>left_maxlevelfound)
  552.         {
  553.             left_maxlevelfound=psamples[0];
  554.         }
  555.         if(psamples[0]<left_minlevelfound)
  556.         {
  557.             left_minlevelfound=psamples[0];
  558.         }
  559.         psamples[0]+=l_dc;
  560.         left_dcsum_out+=psamples[0];
  561.         if(r_inertia)
  562.         {
  563.             r_dc+=r_dc_inc;
  564.             if(!(r_counter--))
  565.             {
  566.                 r_inertia=false;
  567.                 r_dc=r_dc_target;
  568.             }
  569.         }
  570.         right_dcsum_in+=psamples[1];
  571.         if(psamples[1]>right_maxlevelfound)
  572.         {
  573.             right_maxlevelfound=psamples[1];
  574.         }
  575.         if(psamples[1]<right_minlevelfound)
  576.         {
  577.             right_minlevelfound=psamples[1];
  578.         }
  579.         psamples[1]+=r_dc;
  580.         right_dcsum_out+=psamples[1];
  581.         dc_counter++;
  582.         psamples+=2;
  583.     } while(--numsamples);
  584.     }
  585.     }
  586.  
  587.     return true;
  588. }
  589.  
  590. bool mi::MDKWork(float *psamples, int numsamples, int const mode)
  591. {
  592.     if ((mode==WM_WRITE)||(mode==WM_NOIO))
  593.     {
  594.         return false;
  595.     }
  596.  
  597.     if (mode == WM_READ)        // <thru>
  598.         return true;
  599.  
  600.     if(valMode>1) // no stats
  601.     {
  602.     do 
  603.     {
  604.         if(l_inertia)
  605.         {
  606.             l_dc+=l_dc_inc;
  607.             if(!(l_counter--))
  608.             {
  609.                 l_inertia=false;
  610.                 l_dc=l_dc_target;
  611.             }
  612.         }
  613.         psamples[0]+=l_dc;
  614.         psamples++;
  615.     } while(--numsamples);
  616.     } else // stats
  617.     {
  618.     do 
  619.     {
  620.         if(l_inertia)
  621.         {
  622.             l_dc+=l_dc_inc;
  623.             if(!(l_counter--))
  624.             {
  625.                 l_inertia=false;
  626.                 l_dc=l_dc_target;
  627.             }
  628.         }
  629.         left_dcsum_in+=psamples[0];
  630.         if(psamples[0]>left_maxlevelfound)
  631.         {
  632.             left_maxlevelfound=psamples[0];
  633.         }
  634.         if(psamples[0]<left_minlevelfound)
  635.         {
  636.             left_minlevelfound=psamples[0];
  637.         }
  638.         psamples[0]+=l_dc;
  639.         left_dcsum_out+=psamples[0];
  640.         dc_counter++;
  641.         psamples++;
  642.     } while(--numsamples);
  643.     }
  644.  
  645.     return true;
  646. }
  647.